﻿using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Foundation.ExtensionMethods;

namespace Foundation.MarkupExtensions
{
    public class VisibilityMenuItems : MenuGenerator
    {
        private readonly ObservableCollection<MenuItem> _items = new ObservableCollection<MenuItem>();

        private MenuItem _checkMenuItem;
        private MenuItem _uncheckMenuItem;

        protected override ICollection<FrameworkElement> GenerateMenu()
        {
            var items =
                SourceObject is Panel
                    ? SourceObject.GetLogicalChildren().ToList()
                    : (IEnumerable) SourceObject.GetPropertyValue(SourcePropertyName);

            _checkMenuItem = new MenuItem
                {
                    Command = new SimpleCommand(OnExecute, OnCanExecute),
                    CommandParameter = true,
                };
            _checkMenuItem.SetLocalizing(HeaderedItemsControl.HeaderProperty, new Localizing("CheckAll"));
           
            _uncheckMenuItem = new MenuItem
                {
                    Command = new SimpleCommand(OnExecute, OnCanExecute),
                    CommandParameter = false
                };
            _uncheckMenuItem.SetLocalizing(HeaderedItemsControl.HeaderProperty, new Localizing("UncheckAll"));

            _checkMenuItem.PreviewKeyDown += (sender, args) =>
                {
                    if(args.Key != Key.Space && args.Key != Key.Enter) return;
                    _checkMenuItem.Command.Execute(_checkMenuItem.CommandParameter);
                    _checkMenuItem.IsEnabled = _checkMenuItem.Command.CanExecute(_checkMenuItem.CommandParameter);
                    _uncheckMenuItem.IsEnabled = _uncheckMenuItem.Command.CanExecute(_uncheckMenuItem.CommandParameter);
                    args.Handled = true;
                    _uncheckMenuItem.Focus();
                };

            _uncheckMenuItem.PreviewKeyDown += (sender, args) =>
                {
                    if (args.Key != Key.Space && args.Key != Key.Enter) return;
                    _uncheckMenuItem.Command.Execute(_uncheckMenuItem.CommandParameter);
                    _checkMenuItem.IsEnabled = _checkMenuItem.Command.CanExecute(_checkMenuItem.CommandParameter);
                    _uncheckMenuItem.IsEnabled = _uncheckMenuItem.Command.CanExecute(_uncheckMenuItem.CommandParameter);
                    args.Handled = true;
                    _checkMenuItem.Focus();
                };

            _checkMenuItem.IsChecked = true;
            _uncheckMenuItem.IsChecked = true;

            _checkMenuItem.IsEnabledChanged += (sender, args) => _checkMenuItem.IsChecked = !(bool) args.NewValue;
            _uncheckMenuItem.IsEnabledChanged += (sender, args) => _uncheckMenuItem.IsChecked = !(bool) args.NewValue;

            _checkMenuItem.LayoutUpdated += (sender, args) => _checkMenuItem.Command.CanExecute(_uncheckMenuItem.CommandParameter);
            _uncheckMenuItem.LayoutUpdated += (sender, args) => _uncheckMenuItem.Command.CanExecute(_uncheckMenuItem.CommandParameter);

            _checkMenuItem.StaysOpenOnClick = true;
            _uncheckMenuItem.StaysOpenOnClick = true;

            _checkMenuItem.PreviewMouseLeftButtonDown += (sender, args) =>
                {
                    _checkMenuItem.Command.Execute(_checkMenuItem.CommandParameter);
                    _checkMenuItem.IsEnabled = _checkMenuItem.Command.CanExecute(_checkMenuItem.CommandParameter);
                    _uncheckMenuItem.IsEnabled = _uncheckMenuItem.Command.CanExecute(_uncheckMenuItem.CommandParameter);
                };

            _uncheckMenuItem.PreviewMouseLeftButtonDown += (sender, args) =>
                {
                    _uncheckMenuItem.Command.Execute(_uncheckMenuItem.CommandParameter);
                    _checkMenuItem.IsEnabled = _checkMenuItem.Command.CanExecute(_checkMenuItem.CommandParameter);
                    _uncheckMenuItem.IsEnabled = _uncheckMenuItem.Command.CanExecute(_uncheckMenuItem.CommandParameter);
                };

            var contextMenuItems = new ObservableCollection<FrameworkElement>
                {
                    _checkMenuItem,
                    _uncheckMenuItem,
                    new Separator(),
                };

            foreach (var item in items)
            {
                var menuItem = new MenuItem
                    {
                        DataContext = item,
                        Style = ItemContainerStyle,
                    };

                menuItem.PreviewMouseLeftButtonDown += (sender, args) =>
                    {
                        menuItem.IsChecked = !menuItem.IsChecked;
                        _checkMenuItem.IsEnabled = _checkMenuItem.Command.CanExecute(_checkMenuItem.CommandParameter);
                        _uncheckMenuItem.IsEnabled = _uncheckMenuItem.Command.CanExecute(_uncheckMenuItem.CommandParameter);
                        _checkMenuItem.UpdateBindingTargets();
                        _uncheckMenuItem.UpdateBindingTargets();
                        ((FrameworkElement) _checkMenuItem.GetVisualParent()).UpdateLayout();
                    };
                menuItem.PreviewMouseLeftButtonUp += (sender, args) => args.Handled = true;
                menuItem.PreviewKeyDown += (sender, args) =>
                    {
                        if (args.Key != Key.Space && args.Key != Key.Enter) return;
                        menuItem.IsChecked = !menuItem.IsChecked;
                        args.Handled = true;

                        _checkMenuItem.IsEnabled = _checkMenuItem.Command.CanExecute(_checkMenuItem.CommandParameter);
                        _uncheckMenuItem.IsEnabled = _uncheckMenuItem.Command.CanExecute(_uncheckMenuItem.CommandParameter);
                        _checkMenuItem.UpdateBindingTargets();
                        _uncheckMenuItem.UpdateBindingTargets();
                    };

                contextMenuItems.Add(menuItem);
                _items.Add(menuItem);
            }

            return contextMenuItems;
        }

        private bool OnCanExecute(object parameter)
        {
            return !_items.All(i => i.IsChecked.Equals(parameter));
        }

        private void OnExecute(object parameter)
        {
            foreach (var menuItem in _items)
                menuItem.IsChecked = (bool) parameter;
        }

        protected override void OnOpening(object sender, RoutedEventArgs e)
        {
            _checkMenuItem.IsEnabled = _checkMenuItem.Command.CanExecute(_checkMenuItem.CommandParameter);
            _uncheckMenuItem.IsEnabled = _uncheckMenuItem.Command.CanExecute(_uncheckMenuItem.CommandParameter);
        }
    }
}
